package cn.ljy.uem.web.websocket.oauth.lesson;

import cn.ljy.authority.model.SysUser;
import cn.ljy.authority.service.SysUserTokenFrontendService;
import cn.ljy.common.model.result.ResponseData;
import cn.ljy.common.model.result.ResultCode;
import cn.ljy.uem.model.SchoolTeachingTaskLessonMessage;
import cn.ljy.uem.service.SchoolStudentService;
import cn.ljy.uem.service.SchoolTeacherService;
import cn.ljy.uem.service.SchoolTeachingTaskLessonMessageService;
import cn.ljy.uem.service.SchoolTeachingTaskLessonService;
import cn.ljy.uem.service.common.CommonSchoolService;
import cn.ljy.uem.utils.websocket.WebsocketOfSendMessageUtil;
import cn.ljy.uem.web.websocket.config.WebSocketSessionManager;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.List;
import java.util.Map;

/**
 * 课堂聊天室
 * 模拟上课时使用钉钉的场景
 * 因为需要拦截，因此使用handle的方式
 * 消息处理器，用处接收来自客户端的请求
 * 需要继承AbstractWebSocketHandler这个抽象类来实现自己的自定义消息处理器
 * TextWebSocketHandler是用于处理文本消息处理器，也是AbstractWebSocketHandler的派生类
 */
@Slf4j
//@RestController
@Component
public class LessonChatRoomWebSocketHandle extends TextWebSocketHandler {
    @Autowired
    private SchoolTeacherService schoolTeacherService;
    @Autowired
    private SchoolStudentService schoolStudentService;
    @Autowired
    private CommonSchoolService commonSchoolService;
    @Autowired
    private SysUserTokenFrontendService sysUserTokenFrontendService;
    @Autowired
    private SchoolTeachingTaskLessonService schoolTeachingTaskLessonService;
    @Autowired
    private SchoolTeachingTaskLessonMessageService schoolTeachingTaskLessonMessageService;


    //消息模块常用变量
    private String NO_AUTH_VISIT_MESSAGE="您没有访问该课堂的权限，被踢出聊天室";

    /**
     * 当和用户成功建立连接的时候会调用此方法,在此方法内部应该保存连接
     * 说明：
     * 1.当用户连接到课堂聊天室时，如果有该课堂的参与权限，则直接返回该课堂的所有聊天记录
     * 2.如果没有该课堂的参与权限，则直接退出连接。
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        boolean allow=false;//是否允许加入聊天室
        //读取前台token的值
        String token= session.getAttributes().get("token").toString();
        //获取课堂id
        String lessonId= session.getAttributes().get("lessonId").toString();
        try {
            //验证token是否有效，如果有效则刷新token
            sysUserTokenFrontendService.verifyAndRefreshUserToken(token);
            ///获取登录账户信息
            SysUser user=this.getUser(session);
            if(user!=null){
                //验证用户是否有该课堂的访问权限
                if(this.validateLessonAuthority(user,lessonId)){
                    allow=true;
                    // 用户连接成功，放入在线用户缓存
                    WebSocketSessionManager.addSession(session.getId(), session);
                    //根据课堂id获取该课堂聊天室的所有信息
                    List<SchoolTeachingTaskLessonMessage> messages=schoolTeachingTaskLessonMessageService.getAllMessagesByLessonId(lessonId);
                    //将信息返回到本次连接的用户
                    ResponseData responseData=ResponseData.success(user.getNickName()+"进入聊天室",messages);
                    WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(responseData));
                    log.info("新的用户{}加入，当前在线{}人",user.getNickName(),WebSocketSessionManager.getAllSessions().size());
                }
                if(!allow){//如果不允许加入
                    //返回信息
                    WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(ResponseData.error(NO_AUTH_VISIT_MESSAGE)));
                    //关闭socket连接
                    session.close();
                }
            }else{//如果user不存在
                WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(ResponseData.error(ResultCode.UNAUTHORIZED.getCode(),"验证失败：用户已经无效")));
                //关闭socket连接
                session.close();
            }
        } catch (Exception e) {
            WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(ResponseData.error(ResultCode.UNAUTHORIZED.getCode(),"验证失败：token无效，请重新登录")));
            //关闭socket连接
            session.close();
        }
    }

    /**
     * 收到消息的时候会触发该方法
     * 说明：
     * 1.收到消息后，会将该消息发送给同课堂的所有用户
     * @param session 发送消息的用户的 session
     * @param message 发送的内容
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //获取登录账户信息
        SysUser user=this.getUser(session);
        if(user!=null){
            //获取课堂id
            String lessonId= session.getAttributes().get("lessonId").toString();
            //验证用户是否有该课堂的访问权限
            if(this.validateLessonAuthority(user,lessonId)){
                //对消息进行处理
                String messageJson=message.getPayload();
                //如果消息存在，则发送数据给其他用户
                if(StringUtils.hasLength(messageJson)){
                    //将消息转化为实体
                    SchoolTeachingTaskLessonMessage lessonMessage=JSON.parseObject(messageJson,SchoolTeachingTaskLessonMessage.class);
                    //将信息存入数据库
                    try {
                        saveLessonMessage(lessonId,user,lessonMessage);
                    } catch (Exception e) {
                        log.error(e.toString());
                        WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(ResponseData.error("消息保存失败")));
                    }
                    //将消息发送给同课堂的用户
                    sendMessageToAllUserInLesson(lessonId,lessonMessage);
                }
            }else{
                //返回信息
                WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(ResponseData.error(NO_AUTH_VISIT_MESSAGE)));
                //关闭socket连接
                session.close();
            }
        }
    }

    /**
     * 出现异常的时候
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("出现异常");
        //返回信息
        WebsocketOfSendMessageUtil.sendMessageToOne(session,JSON.toJSONString(ResponseData.error("出现异常:"+exception.getMessage())));
    }


    /**
     * 连接关闭后
     * @param session
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        WebSocketSessionManager.removeAndCloseSession(session.getId());
        log.info("用户退出");
    }

    //是否支持分片消息
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 验证用户是否有该课堂的访问权限。
     * 说明：
     * 1.如果是学生用户，必须是该班级/教学任务的学生才能访问
     * 2.如果是教师用户，都可以访问
     * @param user
     * @param lessonId
     * @return
     */
    private boolean validateLessonAuthority(SysUser user,String lessonId){
        boolean flag=true;//默认验证通过
        if(user!=null){
            //判断用户是否有该课堂的参与权限
            try {
                commonSchoolService.validateHaveLessonsAuthority(lessonId,user.getId());
            } catch (Exception e) {
                flag=false;
            }
        }
        return flag;
    }

    /**
     * 通过session读取当前登录用户信息
     * 如果token无效则返回null
     * @param session
     * @return
     */
    private SysUser getUser(WebSocketSession session){
        SysUser user=null;
        //读取前台token的值
        String token= session.getAttributes().get("token").toString();
        //验证token是否有效，如果有效则刷新token
        try {
            sysUserTokenFrontendService.verifyAndRefreshUserToken(token);
            //获取登录账户信息
            user=sysUserTokenFrontendService.getUserFromToken(token);
        } catch (Exception e) {
            log.info("操作失败：token的状态已过期，请重新登录");
        }
        return user;
    }

    /**
     * 发送信息给同一个课堂的其他用户
     * @param lessonId
     * @param lessonMessage
     */
    private void sendMessageToAllUserInLesson(String lessonId,SchoolTeachingTaskLessonMessage lessonMessage){
        String messageJson=JSON.toJSONString(ResponseData.success(lessonMessage));
        //遍历所有在线用户
        for (Map.Entry<String, WebSocketSession> sessionEntry : WebSocketSessionManager.getAllSessions().entrySet()) {
            //找出本课堂的用户。思路：这个学生和发送信息的用户是同一个用户
            WebSocketSession toSession = sessionEntry.getValue();
            //获取该客户端的用户信息
            SysUser user=getUser(toSession);
            if(user!=null){
                try {
                    //验证该用户是否是同一个课堂
                    commonSchoolService.validateHaveLessonsAuthority(lessonId,user.getId());
                    //发送信息给该用户
                    WebsocketOfSendMessageUtil.sendMessageToOne(toSession,messageJson);
                } catch (Exception e) {
                   //如果不是则中止本次循环，访问下一个用户
                    continue;
                }
            }else{
                //如果该客户已经下线（token过期），关闭该session
                WebSocketSessionManager.removeAndCloseSession(toSession.getId());
            }
        }
    }

    /**
     * 将聊天信息存入数据库
     * @param lessonId 课堂主键
     * @param poster 发言者
     * @param lessonMessage
     */
    private void saveLessonMessage(String lessonId,SysUser poster,SchoolTeachingTaskLessonMessage lessonMessage){
        lessonMessage.setLessonId(lessonId);
        lessonMessage.setPoster(poster);
        //存入数据库
        schoolTeachingTaskLessonMessageService.save(lessonMessage);
    }
}
